using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Web.UI;
using System.Web.UI.WebControls.WebParts;
using Microsoft.SharePoint.WebPartPages;
using System.Collections.ObjectModel;
using Microsoft.SharePoint;
using System.Web.UI.WebControls;
using System.Text;
using System.Text.RegularExpressions;
using Microsoft.SharePoint.Administration;

/* SPFilterBasedCQWP
 * 
 * Copyright (C) 2011 Mark Powney
 * 
 * This project is an enhanced version of SharePoint's Content Query WebPart.
 * It enables the user to send filtered data connection values to the 
 * CQWP Filter Fields as specified in it's standard Query parameters. It
 * also enables you to edit the Item and Header XSLT, and CommonViewFields, 
 * allowing quick modifications to how the CQWP renders of content.
 * 
 * The jQuery component included in this project is licensed under it's 
 * respective terms, and is not necessarily covered by the terms of the GPLv2
 * (outlined below).
 * 
 * Full text of the GNU General Public License can be found in the text file
 * contained within this project: gplv2.txt
 */

/* 
 * This program is free software; you can redistribute it and/or modify 
 * it under the terms of the GNU General Public License as published by 
 * the Free Software Foundation; either version 2 of the License, or 
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but 
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 
 * for more details.
 * 
 * You should have received a copy of the GNU General Public License along 
 * with this program; if not, write to the Free Software Foundation, Inc., 
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

namespace SPFilterBasedCQWP
{
    [Guid("abd682c9-d49d-49a4-a16b-0a968a71ea90")]
    public class FilteredCQWP : Microsoft.SharePoint.Publishing.WebControls.ContentByQueryWebPart
    {
        private bool _error = false;
        private string _headerXSLOverride;
        private string _itemXSLOverride;
        private string _commonViewFieldsOverride;

        private string _headerXSLFilename;
        private string _headerXSLWebRelativePath;
        private string _itemXSLFilename;
        private string _itemXSLWebRelativePath;
        private string _thisGuid;

        const string styleLocation = "Style Library/XSL Style Sheets/";
        const string customStyleLocation = "FilteredCQWPStyles";

        List<IFilterValues> _filterProviders = new List<IFilterValues>();

        private string _queryOverrideValue;
        ArrayList _queryOverrideFilter = new ArrayList();

        private string _queryOverrideGroupBy;
        private string _queryOverrideOrderBy;


        #region Intialisation stuff

        public FilteredCQWP()
        {
            this.ExportMode = WebPartExportMode.All;
        }

        protected override void OnInit(EventArgs e)
        {
            // Set up some variables to use throughout the execution and rendering of the Web Part
            _thisGuid = this.StorageKey.ToString();

            // Header XSL file location
            _headerXSLFilename = "HeaderStyle_" + this.StorageKey + ".xsl";
            _headerXSLWebRelativePath = styleLocation + customStyleLocation + "/" + _headerXSLFilename;

            // Item XSL file location
            _itemXSLFilename = "ItemStyle_" + this.StorageKey + ".xsl";
            _itemXSLWebRelativePath = styleLocation + customStyleLocation + "/" + _itemXSLFilename;

            // Check if the header or item files exist, if so overwrite the CQWP's XslLink properties
            if (SPContext.Current.Site.RootWeb.GetFile(_itemXSLWebRelativePath).Exists)
            {
                _itemXSLOverride = SPContext.Current.Site.RootWeb.GetFileAsString(_itemXSLWebRelativePath);
                this.ItemXslLink = "/" + _itemXSLWebRelativePath;
            }
            if (SPContext.Current.Site.RootWeb.GetFile(_headerXSLWebRelativePath).Exists)
            {
                _headerXSLOverride = SPContext.Current.Site.RootWeb.GetFileAsString(_headerXSLWebRelativePath);
                this.HeaderXslLink = "/" + _headerXSLWebRelativePath;
            }

            // Hard code what style templates to use (need to be smarter about this for a future release
            this.ItemStyle = "Default";
            this.GroupStyle = "DefaultHeader";

            // Overwrite the CQWP's CommonViewFields with the web part's custom property
            this.CommonViewFields = this.CommonViewFieldsOverride;

            this.QueryOverride = this.QueryOverrideValue;

            // Do all the usual stuff
            base.OnInit(e);

        }

        #endregion

        #region Custom Properties

        public string CommonViewFieldsOverride
        {
            get
            {
                if (_commonViewFieldsOverride == null)
                {
                    _commonViewFieldsOverride = "";
                }
                return _commonViewFieldsOverride;
            }
            set
            {
                _commonViewFieldsOverride = value;
                this.CommonViewFields = _commonViewFieldsOverride;
            }
        }
        
        public string ItemXSLOverride
        {
            get
            {
                if (_itemXSLOverride == null)
                {
                    // If the string hasn't been filled out yet, grab appropriate content from the DefaultItemContent function
                    _itemXSLOverride = this.DefaultItemContent();
                }
                return _itemXSLOverride;
            }
            set 
            {
                try
                {
                    // Check that a change to the Item XSL value was made, and for some sanity
                    if (_itemXSLOverride != value && _itemXSLWebRelativePath != null)
                    {
                        // Save the Item XSL content into the Item Styles file
                        this.SaveXSLFile(_itemXSLWebRelativePath, value);
                        _itemXSLOverride = value;
                        this.ItemXslLink = ("/" + _itemXSLWebRelativePath);
                    }
                }
                catch (Exception ex)
                {
                    throw new SPException("Error while saving Item XSL file: " + ex.Message);
                }
            }

        }

        public string HeaderXSLOverride
        {
            get
            {
                if (_headerXSLOverride == null)
                {
                    // If the string hasn't been filled out yet, grab appropriate content from the DefaultHeaderContent function
                    _headerXSLOverride = this.DefaultHeaderContent();
                }
                return _headerXSLOverride;
            }
            set {

                try
                {
                    // Check that a change to the Header XSL value was made, and for some sanity
                    if (_headerXSLOverride != value && _headerXSLWebRelativePath != null)
                    {

                        // Save the Header XSL content into the Item Styles file
                        this.SaveXSLFile(_headerXSLWebRelativePath, value);
                        this.HeaderXslLink = ("/" + _headerXSLWebRelativePath);
                        _headerXSLOverride = value;
                    }
                }
                catch (Exception ex)
                {
                    throw new SPException("Error while saving Header XSL file: " + ex.Message);
                }
            }
        }

        public string QueryOverrideGroupBy
        {
            get
            {
                if (_queryOverrideGroupBy == null) _queryOverrideGroupBy = "";
                return _queryOverrideGroupBy;
            }
            set
            {
                if (value == "") _queryOverrideGroupBy = null;
                else _queryOverrideGroupBy = value;
                this.GroupBy = _queryOverrideGroupBy;

            }
        }

        public string QueryOverrideOrderBy
        {
            get
            {
                if (_queryOverrideOrderBy == null) _queryOverrideOrderBy = "";
                return _queryOverrideOrderBy;
            }
            set
            {
                if (value == "") _queryOverrideOrderBy = null;
                else _queryOverrideOrderBy = value;
                this.SortBy = _queryOverrideOrderBy;
            }
        }

        [WebBrowsable(true), Personalizable(PersonalizationScope.Shared)]
        public ArrayList QueryOverrideFilters
        {
            get
            {
                if (_queryOverrideFilter == null)
                {
                    _queryOverrideFilter = new ArrayList();
                }
                return _queryOverrideFilter;
            }
            set
            {
                _queryOverrideFilter = value;
                this.GenerateQueryOverrideValue(_queryOverrideFilter);
            }
        }

        public string QueryOverrideValue
        {
            get
            {
                if (_queryOverrideValue == null)
                {
                    _queryOverrideValue = "";
                }
                return _queryOverrideValue;
            }
            set
            {
                _queryOverrideValue = value;
            }
        }

        private void GenerateQueryOverrideValue(ArrayList inputQueryFilters)
        {
            try
            {
                string generatedQuery = "";
                /*string groupByComponent = "";
                string orderByComponent = "";*/

                if (inputQueryFilters.Count > 0)
                {
                    generatedQuery = "<Where>" + (char)13;
                    string[] comparisons = new string[inputQueryFilters.Count];
                    string[] clauses = new string[inputQueryFilters.Count];
                    for (int x = 0; x < inputQueryFilters.Count; x++)
                    {
                        QueryOverrideFilter thisFilter = (QueryOverrideFilter)inputQueryFilters[x];
                        if (x < inputQueryFilters.Count - 1)
                        {
                            switch (thisFilter.Comparison)
                            {
                                case QueryOverrideFilter.ComparisonTypes.And: comparisons[x] = "<And>" + (char)13; break;
                                case QueryOverrideFilter.ComparisonTypes.Or: comparisons[x] = "<Or>" + (char)13; break;
                            }
                        }

                        string thisClauseField = "<FieldRef Name='" + thisFilter.Field + "'/>";
                        string thisClauseValue = "<Value Type='" + thisFilter.Type + "'>" + thisFilter.Value + "</Value>";
                        switch (thisFilter.Operator)
                        {
                            case QueryOverrideFilter.OperatorTypes.EqualTo: clauses[x] = "<Eq>" + thisClauseField + thisClauseValue + "</Eq>"; break;
                            case QueryOverrideFilter.OperatorTypes.NotEqualTo: clauses[x] = "<Neq>" + thisClauseField + thisClauseValue + "</Neq>"; break;
                            case QueryOverrideFilter.OperatorTypes.GreaterThan: clauses[x] = "<Gt>" + thisClauseField + thisClauseValue + "</Gt>"; break;
                            case QueryOverrideFilter.OperatorTypes.GreaterThanOrEqualTo: clauses[x] = "<Geq>" + thisClauseField + thisClauseValue + "</Geq>"; break;
                            case QueryOverrideFilter.OperatorTypes.LessThan: clauses[x] = "<Lt>" + thisClauseField + thisClauseValue + "</Lt>"; break;
                            case QueryOverrideFilter.OperatorTypes.LessThanOrEqualTo: clauses[x] = "<Leq>" + thisClauseField + thisClauseValue + "</Leq>"; break;
                            case QueryOverrideFilter.OperatorTypes.BeginsWith: clauses[x] = "<BeginsWith>" + thisClauseField + thisClauseValue + "</BeginsWith>"; break;
                            case QueryOverrideFilter.OperatorTypes.Contains: clauses[x] = "<Contains>" + thisClauseField + thisClauseValue + "</Contains>"; break;
                            case QueryOverrideFilter.OperatorTypes.DateRangesOverlap: clauses[x] = "<DateRangesOverlap>" + thisClauseField + thisClauseValue + "</DateRangesOverlap>"; break;
                            case QueryOverrideFilter.OperatorTypes.Includes: clauses[x] = "<Includes>" + thisClauseField + thisClauseValue + "</Includes>"; break;
                            case QueryOverrideFilter.OperatorTypes.NotIncludes: clauses[x] = "<NotIncludes>" + thisClauseField + thisClauseValue + "</NotIncludes>"; break;
                            case QueryOverrideFilter.OperatorTypes.IsNull: clauses[x] = "<IsNull>" + thisClauseField + "</IsNull>"; break;
                            case QueryOverrideFilter.OperatorTypes.IsNotNull: clauses[x] = "<IsNotNull>" + thisClauseField + "</IsNotNull>"; break;
                        }

                        generatedQuery += comparisons[x] + clauses[x] + (char)13;

                    }

                    for (int x = inputQueryFilters.Count - 1; x >= 0; x--)
                    {
                        if (comparisons[x] != null) generatedQuery += comparisons[x].Replace("<", "</");
                    }

                    generatedQuery += "</Where>";

                }

                /*int groupByCount = 0;
                foreach (string thisGroupBy in this.QueryOverrideGroupBy.Split(','))
                {
                    string trimmedGroupBy = thisGroupBy.Trim();
                    if (trimmedGroupBy != "")
                    {
                        groupByComponent += "<FieldRef Name='" + trimmedGroupBy + "'/>" + (char)13;
                        groupByCount++;
                    }
                }
                if (groupByCount > 0)
                    generatedQuery += "<GroupBy>" + (char)13 + groupByComponent + "</GroupBy>" + (char)13;

                int orderByCount = 0;
                foreach (string thisOrderBy in this.QueryOverrideOrderBy.Split(','))
                {
                    string trimmedOrderBy = thisOrderBy.Trim();
                    if (trimmedOrderBy != "")
                    {
                        orderByComponent += "<FieldRef Name='" + trimmedOrderBy + "' Ascending='false' />" + (char)13;
                        orderByCount++;
                    }
                }
                if (orderByCount > 0)
                    this. += "<OrderBy>" + (char)13 + orderByComponent + "</OrderBy>" + (char)13;

                if (generatedQuery.Length > 0) generatedQuery = "<Query>" + (char)13 + generatedQuery + "</Query>" + (char)13;
                */
                this.QueryOverrideValue = generatedQuery;
            }
            catch (Exception ex)
            {
                this.Controls.Add(new LiteralControl("<span style='color: red;'>Error occurred while generating query override: " + ex.Message + "</span>"));
            }

        }

        private void SaveXSLFile(string webRelativefilePath, string fileContents)
        {
            try
            {

                // Open the current site collection
                SPSite thisSite = new SPSite(SPContext.Current.Site.Url);

                // Using OpenWeb so this object is it's own (and not the one used for the rest of the SharePoint content rendering
                SPWeb thisRootWeb = thisSite.OpenWeb("/");

                // Grab a reference to the custom Item Styles folder
                SPFolder customStyleFolder = thisRootWeb.GetFolder(styleLocation + customStyleLocation);

                // Check if the custom Item Styles folder exists
                if (customStyleFolder.Exists == false)
                {

                    // If it doesn't, create the folder
                    SPFolder rootStyleFolder = thisRootWeb.GetFolder(styleLocation);
                    customStyleFolder = rootStyleFolder.SubFolders.Add(customStyleLocation);
                    customStyleFolder.Update();
                    rootStyleFolder.Update();

                    // Now that the folder's created, get a reference to it
                    customStyleFolder = thisRootWeb.GetFolder(styleLocation + customStyleLocation);
                }

                // Check for some sanity in the parameters passed in
                if (webRelativefilePath != null)
                {

                    // Get a reference to the file that needs to be saved
                    SPFile thisItemFile = thisRootWeb.GetFile(webRelativefilePath);

                    // Check if the file exists
                    if (thisItemFile.Exists)

                        // Check if the file's checkout status is Checked In
                        if (thisItemFile.CheckOutStatus == SPFile.SPCheckOutStatus.None)
                        {

                            // If it is checked in, check it out
                            thisItemFile.CheckOut();
                        }
                        else
                        {
                            // If it is checked out, determine if the file is checked out to the current user
                            if (thisItemFile.CheckedOutBy.LoginName != SPContext.Current.Web.CurrentUser.LoginName)
                                throw new SPException("The file [" + webRelativefilePath + "] is currently checked out by [" + thisItemFile.CheckedOutBy.LoginName + "].");
                        }

                    // Encode the user's XSL content, and save it to the referenced file
                    UTF8Encoding thisEncoding = new System.Text.UTF8Encoding();
                    byte[] xslBytes = thisEncoding.GetBytes(fileContents);
                    thisItemFile.SaveBinary(xslBytes);
                    //thisItemFile.Update();

                    // Check the file in, and publish it, with some comments to help determine where the file came from
                    thisItemFile.CheckIn("Version checked in after modifying the WebPart properties on the page [" + this.Page.Request.Url.AbsoluteUri + "]");
                    thisItemFile.Publish("Version published after modifying the [" + this.Title + "] WebPart properties on the page [" + this.Page.Request.Url.AbsoluteUri + "]");
                    thisItemFile.Update();
                }

                // Do some manual cleaning up
                thisRootWeb.Dispose();
                thisSite.Dispose();
            }
            catch (Exception ex)
            {
                throw new SPException("Error occurred while saving XSL file [" + webRelativefilePath + "]: " + ex.Message);
            }
        }

        #endregion

        #region Basic customised WebPart stuff

        protected override void OnLoad(EventArgs e)
        {
            if (!_error)
            {
                try
                {
                    base.OnLoad(e);
                    //this.EnsureChildControls();

                    // Your code here...
                }
                catch (Exception ex)
                {
                    HandleException(ex);
                }
            }
        }

        /// <summary>
        /// Clear all child controls and add an error message for display.
        /// </summary>
        /// <param name="ex"></param>
        private void HandleException(Exception ex)
        {
            this._error = true;
            this.Controls.Clear();
            this.Controls.Add(new LiteralControl(ex.Message));
        }

        #endregion

        #region Guts of the CQWP enhancements

        protected override void CreateChildControls()
        {

            if (!_error)
            {
                try
                {

                    // First, determine if there's a filter applied
                    bool shouldFilter = true;
                    if (this._filterProviders.Count == 0) shouldFilter = false;
                    else if (this._filterProviders.Count > 0)
                    {
                        // Iterate through each of the filter parameters sent in
                        foreach (IFilterValues filter in this._filterProviders)
                            if (filter.ParameterValues == null)
                            {
                                // Some filter sent "All" - don't bother with the rest
                                // of the filtering.
                                shouldFilter = false;
                                break;
                            }
                    }

                    // Check if a filter has been applied
                    if (shouldFilter)

                        // Iterate through each of the filter parameters sent in
                        foreach (IFilterValues thisProvider in this._filterProviders)
                        {

                            // Iterate through each of the values sent in under this parameter
                            ReadOnlyCollection<String> providerValues = thisProvider.ParameterValues;

                            // Check the name of the parameter begins with "Override Filter Field"
                            if (thisProvider.ParameterName.Length > 21 && thisProvider.ParameterName.Substring(0, 21).ToLower() == "override filter field" && providerValues != null)
                            {
                                foreach (string thisValue in providerValues)
                                {
                                    if (thisValue != null)
                                    {
                                        // Work out which filter number this parameter value applies to
                                        int thisFilterNumber = 0;
                                        try
                                        {
                                            string thisFilterNumberString = thisProvider.ParameterName.Substring(22, 1);
                                            thisFilterNumber = int.Parse(thisFilterNumberString);
                                        }
                                        catch (Exception ex)
                                        { }

                                        // Overwrite the CQWP's FilterValue property with the value sent in by this parameter
                                        switch (thisFilterNumber)
                                        {
                                            case 1: this.FilterValue1 = thisValue; break;
                                            case 2: this.FilterValue2 = thisValue; break;
                                            case 3: this.FilterValue3 = thisValue; break;
                                        }
                                    }
                                }
                            }
                            if (thisProvider.ParameterName.Length > 23 && thisProvider.ParameterName.Substring(0, 22).ToLower() == "query override filter " && providerValues != null)
                            {

                                string currentQueryOverride = this.QueryOverrideValue;

                                foreach (string thisValue in providerValues)
                                {
                                    if (thisValue != null)
                                    {
                                        // Work out which filter number this parameter value applies to
                                        string thisFilterString = thisProvider.ParameterName.Substring(22);
                                        currentQueryOverride = currentQueryOverride.Replace(thisFilterString, thisValue);
                                    }
                                }

                                this.QueryOverride = currentQueryOverride;
                            }

                        }

                    // Do the usual CreateChildControls
                    base.CreateChildControls();

                }
                catch (Exception ex)
                {
                    HandleException(ex);
                }
            }
        }

        #region Data connector / filtering definition

        [ConnectionConsumer("CQWP Filter Field", "FilterField")]
        public void SetConnectionInterface(IFilterValues filterProvider)
        {
          if (filterProvider != null)
          {

              List<ConsumerParameter> l = new List<ConsumerParameter>();

              string filterFieldSet1 = "";
              string filterFieldSet2 = "";
              string filterFieldSet3 = "";

              if (this.FilterField1 == string.Empty) filterFieldSet1 = " (not specified)"; else filterFieldSet1 = " (" + this.FilterType1 + ")";
              if (this.FilterField2 == string.Empty) filterFieldSet2 = " (not specified)"; else filterFieldSet2 = " (" + this.FilterType2 + ")";
              if (this.FilterField3 == string.Empty) filterFieldSet3 = " (not specified)"; else filterFieldSet3 = " (" + this.FilterType3 + ")";

              l.Add(new ConsumerParameter("Override Filter Field 1" + filterFieldSet1, ConsumerParameterCapabilities.SupportsSingleValue | ConsumerParameterCapabilities.SupportsEmptyValue | ConsumerParameterCapabilities.SupportsAllValue));
              l.Add(new ConsumerParameter("Override Filter Field 2" + filterFieldSet2, ConsumerParameterCapabilities.SupportsSingleValue | ConsumerParameterCapabilities.SupportsEmptyValue | ConsumerParameterCapabilities.SupportsAllValue));
              l.Add(new ConsumerParameter("Override Filter Field 3" + filterFieldSet3, ConsumerParameterCapabilities.SupportsSingleValue | ConsumerParameterCapabilities.SupportsEmptyValue | ConsumerParameterCapabilities.SupportsAllValue));

              Regex thisRegEx = new Regex(@"\[(\w*)\]");
              MatchCollection foundMatches = thisRegEx.Matches(this.QueryOverrideValue);
              foreach (Match thisMatch in foundMatches)
              {
                  l.Add(new ConsumerParameter("Query Override Filter [" + thisMatch.Groups[1].Value + "]", ConsumerParameterCapabilities.SupportsSingleValue | ConsumerParameterCapabilities.SupportsEmptyValue | ConsumerParameterCapabilities.SupportsAllValue));
              }


              filterProvider.SetConsumerParameters(new ReadOnlyCollection<ConsumerParameter>(l));

              this._filterProviders.Add(filterProvider);

          }

        }

        #endregion

        #endregion

        #region Query Override Filter sub-class

        [Serializable]
        public class QueryOverrideFilter
        {
            public enum ComparisonTypes {And, Or};
            public enum OperatorTypes {
                EqualTo, 
                NotEqualTo, 
                GreaterThan, 
                GreaterThanOrEqualTo, 
                LessThan, 
                LessThanOrEqualTo, 
                BeginsWith, 
                Contains,
                DateRangesOverlap,
                IsNotNull,
                IsNull,
                Includes,
                NotIncludes
            }

            private string _fieldType;

            public ListItem[] PossibleOperators
            {
                get
                {
                    bool includeSp2010Options = false;
                    int listItemCount = 11;

                    if (SPFarm.Local.BuildVersion.Major != 12)
                    {
                        includeSp2010Options = true;
                        listItemCount = 13;
                    }
                    
                    ListItem[] thisListItemArray = new ListItem[listItemCount];

                    thisListItemArray[(int)OperatorTypes.EqualTo] = new ListItem("is Equal to", OperatorTypes.EqualTo.ToString());
                    thisListItemArray[(int)OperatorTypes.NotEqualTo] = new ListItem("is Not Equal to", OperatorTypes.NotEqualTo.ToString());
                    thisListItemArray[(int)OperatorTypes.GreaterThan] = new ListItem("is Greater than", OperatorTypes.GreaterThan.ToString());
                    thisListItemArray[(int)OperatorTypes.GreaterThanOrEqualTo] = new ListItem("is Greater than or Equal to", OperatorTypes.GreaterThanOrEqualTo.ToString());
                    thisListItemArray[(int)OperatorTypes.LessThan] = new ListItem("is Less than", OperatorTypes.LessThan.ToString());
                    thisListItemArray[(int)OperatorTypes.LessThanOrEqualTo] = new ListItem("is Lass than or Equal to", OperatorTypes.LessThanOrEqualTo.ToString());
                    thisListItemArray[(int)OperatorTypes.BeginsWith] = new ListItem("Begins with", OperatorTypes.BeginsWith.ToString());
                    thisListItemArray[(int)OperatorTypes.Contains] = new ListItem("Contains", OperatorTypes.Contains.ToString());
                    thisListItemArray[(int)OperatorTypes.DateRangesOverlap] = new ListItem("Overlaps with date", OperatorTypes.DateRangesOverlap.ToString());
                    thisListItemArray[(int)OperatorTypes.IsNotNull] = new ListItem("Is not null", OperatorTypes.IsNotNull.ToString());
                    thisListItemArray[(int)OperatorTypes.IsNull] = new ListItem("Is null", OperatorTypes.IsNull.ToString());
                    if (includeSp2010Options)
                    {
                        thisListItemArray[(int)OperatorTypes.Includes] = new ListItem("Includes value", OperatorTypes.Includes.ToString());
                        thisListItemArray[(int)OperatorTypes.NotIncludes] = new ListItem("Doesn't include value", OperatorTypes.NotIncludes.ToString());
                    }
                    return thisListItemArray;
                }
            }

            public string Field {get; set;}
            public string Type 
            { 
                get 
                {
                    if (_fieldType == null && Field != "")
                    {
                        foreach (SPWeb thisWeb in SPContext.Current.Site.AllWebs)
                        {
                            if (_fieldType != null) break;
                            foreach (SPList thisList in thisWeb.Lists)
                            {
                                if (_fieldType != null) break;
                                foreach (SPField thisField in thisList.Fields)
                                {
                                    if (thisField.StaticName == Field || thisField.Title == Field || thisField.InternalName == Field)
                                    {
                                        switch (thisField.Type)
                                        {
                                            // TODO: Add all SPFieldTypes to this list.
                                            case SPFieldType.Text: _fieldType = "Text"; break;
                                            case SPFieldType.Number: _fieldType = "Number"; break;
                                            case SPFieldType.DateTime: _fieldType = "DateTime"; break;
                                            case SPFieldType.Integer: _fieldType = "Integer"; break;
                                            case SPFieldType.Computed: _fieldType = "Computed"; break;
                                            case SPFieldType.URL: _fieldType = "URL"; break;
                                            case SPFieldType.Lookup: _fieldType = "Lookup"; break;
                                            case SPFieldType.Counter: _fieldType = "Counter"; break;
                                            default: _fieldType = "Text"; break;
                                        }
                                        break;
                                    }
                                }
                            }
                        }

                        if (_fieldType == null) _fieldType = "Text";
                    }

                    return _fieldType;
                }
                set
                {
                    _fieldType = value;
                }
            }
            public OperatorTypes Operator {get; set;}
            public string Value {get; set;}
            public ComparisonTypes Comparison { get; set; }
        }

        #endregion



        #region Custom Tool Panel for user to modify properties in the UI

        // Override the GetToolParts method, to add our Custom Tool Part to the end of the standard tool parts
        public override ToolPart[] GetToolParts()
        {

            List<ToolPart> customToolPart = new List<ToolPart>(base.GetToolParts());
            try
            {
                customToolPart.Add(new XSLTemplateToolPart());
                customToolPart.Add(new QueryOverrideToolPart());

            }
            catch (Exception ex)
            {
                this.Controls.Add(new LiteralControl("<span style='color: red;'>Error occured when getting tool parts: " + ex.Message));
            }

            return customToolPart.ToArray();

        }

        // Now - start defining our Custom Tool Part
        public class XSLTemplateToolPart : Microsoft.SharePoint.WebPartPages.ToolPart
        {
            TextBox txtItemXslContent;
            TextBox txtHeaderXslContent;
            TextBox txtCommonViewFields;

            Panel toolPartPanel;
            FilteredCQWP thisWebPart;

            protected override void CreateChildControls()
            {

                // Get a reference to the parent Web Part
                thisWebPart = (FilteredCQWP)ParentToolPane.SelectedWebPart;

                // Name of this tool part is "XSL Definition"
                this.Title = "XSL Definition";

                // Set up a new Panel for use in this tool part
                toolPartPanel = new Panel();

                // Set up some text boxes for getting custom properties
                txtHeaderXslContent = new TextBox();
                txtHeaderXslContent.ID = "XslHeaderContent";
                txtHeaderXslContent.TextMode = TextBoxMode.MultiLine;
                txtHeaderXslContent.Width = Unit.Pixel(580);
                txtHeaderXslContent.Height = Unit.Pixel(380);
                txtHeaderXslContent.Text = thisWebPart.HeaderXSLOverride;

                txtItemXslContent = new TextBox();
                txtItemXslContent.ID = "XslItemContent";
                txtItemXslContent.TextMode = TextBoxMode.MultiLine;
                txtItemXslContent.Width = Unit.Pixel(580);
                txtItemXslContent.Height = Unit.Pixel(380);
                txtItemXslContent.Text = thisWebPart.ItemXSLOverride;

                txtCommonViewFields = new TextBox();
                txtCommonViewFields.ID = "XslCommonViewFields";
                txtCommonViewFields.CssClass = "ms-input";
                txtCommonViewFields.Style.Add("width", "90%");
                txtCommonViewFields.Width = Unit.Pixel(200);
                txtCommonViewFields.Text = thisWebPart.CommonViewFieldsOverride;

                // Add some buttons to the UI, so the "big" textboxes can be opened
                toolPartPanel.Controls.Add(new LiteralControl("<div id='XslDefinitionToolPartContainer' style='text-align: center;'>"));
                toolPartPanel.Controls.Add(new LiteralControl("<input type='button' class='UserButton' style='margin: 4px;' onClick=\"javascript:openEditBox('EditXslHeaderContent')\" value=\"Group By XSL Template\"/><br/>"));
                toolPartPanel.Controls.Add(new LiteralControl("<input type='button' class='UserButton' style='margin: 4px;' onClick=\"javascript:openEditBox('EditXslItemContent')\" value=\"Item XSL Template\"/>"));

                // Render out the "big" textboxes, with div's to allow them to be floated and hidden as required
                toolPartPanel.Controls.Add(new LiteralControl("<div id='EditXslHeaderContent' style='display: none; float: left; position: absolute; right: 100px; top: 200px; padding: 8px; width: 600px; height: 400px; background-color: #eee; border: 1px solid #aaa;'>"));
                toolPartPanel.Controls.Add(txtHeaderXslContent);
                toolPartPanel.Controls.Add(new LiteralControl("<input type='button' value='Done' class='UserButton' onClick=\"javascript:closeEditBox('XslDefinitionToolPartContainer', 'EditXslHeaderContent');\"/>"));
                toolPartPanel.Controls.Add(new LiteralControl("<span style='text-align: right; width: 530px; position: absolute; font-weight: bold; font-size: 10pt;';>Group By Header XSL</span>"));
                toolPartPanel.Controls.Add(new LiteralControl("</div>"));

                toolPartPanel.Controls.Add(new LiteralControl("<div id='EditXslItemContent' style='display: none; float: left; position: absolute; right: 100px; top: 200px; padding: 8px; width: 600px; height: 400px; background-color: #eee; border: 1px solid #aaa;'>"));
                toolPartPanel.Controls.Add(txtItemXslContent);
                toolPartPanel.Controls.Add(new LiteralControl("<input type='button' value='Done' class='UserButton' onClick=\"javascript:closeEditBox('XslDefinitionToolPartContainer', 'EditXslItemContent');\"/>"));
                toolPartPanel.Controls.Add(new LiteralControl("<span style='text-align: right; width: 530px; position: absolute; font-weight: bold; font-size: 10pt;';>Item XSL</span><br/>"));
                toolPartPanel.Controls.Add(new LiteralControl("</div>"));

                toolPartPanel.Controls.Add(new LiteralControl("</div>"));

                // Add the Common View Fields heading and text box
                toolPartPanel.Controls.Add(new LiteralControl("<br/><strong>Common View Fields:</strong><br/>"));
                toolPartPanel.Controls.Add(txtCommonViewFields);

                // Add the panel to this Tool Part's controls
                Controls.Add(toolPartPanel);

                // Add a reference to jQuery in the page's header
                this.Page.Header.Controls.Add(new LiteralControl("<script type=\"text/javascript\" language=\"javascript\" src=\"/_layouts/yarracity/jquery-1.5.min.js\"></script>"));

                // Add some of our own javascript to enable "Big" text box showing and hiding
                this.Controls.Add(new LiteralControl("<script type=\"text/javascript\" language=\"javascript\">"
                    + "function openEditBox(whichBox) {"
                        + "resetEditBoxes();"
                        + "$('#mpdmconsole').append($('#' + whichBox));"
                        + "$('#' + whichBox).centre();"
                        + "$('#' + whichBox).show();"
                        + "$('#' + whichBox + ' textarea:first-child').focus();"
                    + "};"
                    + "function closeEditBox(whichContainer, whichBox) {"
                        + "$('#' + whichContainer).append($('#' + whichBox));"
                        + "$('#' + whichBox).hide();"
                    + "};"
                    + "function resetEditBoxes() {"
                        + "closeEditBox('XslDefinitionToolPartContainer', 'EditXslItemContent');"
                        + "closeEditBox('XslDefinitionToolPartContainer', 'EditXslHeaderContent');"
                        + "closeEditBox('QueryOverrideContainer', 'EditQueryOverrideContent');"
                    + "};"
                    + "jQuery.fn.centre = function () {"
                    //+ "    this.css('position','absolute');"
                    + "    this.css('top', ( $(window).height() - this.height() ) / 2+$(window).scrollTop() + 'px');"
                    + "    this.css('left', ( $(window).width() - this.width() ) / 2+$(window).scrollLeft() + 'px');"
                    + "    return this;"
                    + "}"
                    + "</script>"));
                
                
                // Do all the usual Child controls
                base.CreateChildControls();

            }

            public override void ApplyChanges()
            {

                try
                {

                    // Get a reference to the parent Web Part
                    thisWebPart = (FilteredCQWP)ParentToolPane.SelectedWebPart;

                    // Set the parent Web Part's custom properties to the contents of the text boxes of this Tool Panel
                    thisWebPart.ItemXSLOverride = txtItemXslContent.Text;
                    thisWebPart.HeaderXSLOverride = txtHeaderXslContent.Text;
                    thisWebPart.CommonViewFieldsOverride = txtCommonViewFields.Text;

                }
                catch (Exception ex)
                {
                    Controls.Add(new LiteralControl("<span style=\"color: #f00;\">Error: " + ex.Message + "</span>"));
                }
                base.ApplyChanges();
            }


        }

        public class QueryOverrideToolPart : Microsoft.SharePoint.WebPartPages.ToolPart
        {
            TextBox[] txtFilterFieldName;
            TextBox[] txtFilterFieldType;
            DropDownList[] dpdFilterOperator;
            TextBox[] txtFilterValue;
            RadioButton[] radFilterAnd;
            RadioButton[] radFilterOr;
            Button[] btnRemove;

            TextBox txtNewFilterFieldName = new TextBox();
            TextBox txtNewFilterValue = new TextBox();
            DropDownList dpdNewFilterOperator = new DropDownList();
            Button btnAdd = new Button();
            TextBox txtQueryOverrideContent = new TextBox();

            TextBox txtGroupByFields = new TextBox();
            TextBox txtOrderByFields = new TextBox();

            Panel toolPartPanel;
            FilteredCQWP thisWebPart;
            //HiddenField hidApplyChanges = new HiddenField();

            protected override void CreateChildControls()
            {

                this.Title = "Query Override Filters";

                try
                {

                    txtFilterFieldName = new TextBox[100];
                    txtFilterFieldType = new TextBox[100];
                    dpdFilterOperator = new DropDownList[100];
                    txtFilterValue = new TextBox[100];
                    radFilterAnd = new RadioButton[100];
                    radFilterOr = new RadioButton[100];
                    btnRemove = new Button[100];

                    // Get a reference to the parent Web Part
                    thisWebPart = (FilteredCQWP)ParentToolPane.SelectedWebPart;

                    toolPartPanel = new Panel();

                    txtQueryOverrideContent = new TextBox();
                    txtQueryOverrideContent.ID = "txtQueryOverrideContent";
                    txtQueryOverrideContent.TextMode = TextBoxMode.MultiLine;
                    txtQueryOverrideContent.Width = Unit.Pixel(580);
                    txtQueryOverrideContent.Height = Unit.Pixel(380);
                    txtQueryOverrideContent.Text = thisWebPart.QueryOverrideValue;
                    txtQueryOverrideContent.Enabled = false;

                    toolPartPanel.Controls.Add(new LiteralControl("<div id='QueryOverrideParameters' style='text-align: center;'>"));

                    txtGroupByFields.CssClass = "ms-input";
                    txtGroupByFields.Style.Add("width", "90%");
                    txtGroupByFields.ID = "txtGroupByFields";
                    txtGroupByFields.Text = thisWebPart.QueryOverrideGroupBy;

                    txtOrderByFields.CssClass = "ms-input";
                    txtOrderByFields.Style.Add("width", "90%");
                    txtOrderByFields.ID = "txtOrderByFields";
                    txtOrderByFields.Text = thisWebPart.QueryOverrideOrderBy;

                    toolPartPanel.Controls.Add(new LiteralControl("<br/><div style='font-weight: bold; text-align: left; width: 90%;'>Group by:</div>"));
                    toolPartPanel.Controls.Add(txtGroupByFields);
                    toolPartPanel.Controls.Add(new LiteralControl("<div style='font-weight: bold; text-align: left; width: 90%;'>Order by:</div>"));
                    toolPartPanel.Controls.Add(txtOrderByFields);
                    toolPartPanel.Controls.Add(new LiteralControl("<div style='width:90%' class='UserDottedLine'></div>"));

                    if (thisWebPart.QueryOverrideFilters.Count == 0)
                    {
                        toolPartPanel.Controls.Add(new LiteralControl("(no filters currently defined)<br/>"));
                    }
                    else
                    {
                        toolPartPanel.Controls.Add(new LiteralControl("<br/><strong>Current Filters:</strong><br/>"));
                    }

                    int filterCounter = -1;

                    foreach (QueryOverrideFilter thisFilter in thisWebPart.QueryOverrideFilters)
                    {
                        filterCounter++;
                        txtFilterFieldName[filterCounter] = new TextBox();
                        txtFilterFieldName[filterCounter].CssClass = "ms-input";
                        txtFilterFieldName[filterCounter].Style.Add("width", "90%");
                        txtFilterFieldName[filterCounter].ID = "txtFilterFieldName" + filterCounter;
                        txtFilterFieldName[filterCounter].Text = thisFilter.Field;

                        txtFilterFieldType[filterCounter] = new TextBox();
                        txtFilterFieldType[filterCounter].CssClass = "ms-input";
                        txtFilterFieldType[filterCounter].Style.Add("width", "90%");
                        txtFilterFieldType[filterCounter].ID = "txtFilterFieldType" + filterCounter;
                        txtFilterFieldType[filterCounter].Text = thisFilter.Type;

                        dpdFilterOperator[filterCounter] = new DropDownList();
                        dpdFilterOperator[filterCounter].ID = "dpdFilterOperator" + filterCounter;
                        dpdFilterOperator[filterCounter].Style.Add("width", "90%");
                        dpdFilterOperator[filterCounter].Items.AddRange(thisFilter.PossibleOperators);
                        dpdFilterOperator[filterCounter].SelectedValue = thisFilter.Operator.ToString();

                        txtFilterValue[filterCounter] = new TextBox();
                        txtFilterValue[filterCounter].CssClass = "ms-input";
                        txtFilterValue[filterCounter].Style.Add("width", "90%");
                        txtFilterValue[filterCounter].ID = "txtFilterValue" + filterCounter;
                        txtFilterValue[filterCounter].Text = thisFilter.Value;

                        radFilterAnd[filterCounter] = new RadioButton();
                        radFilterAnd[filterCounter].ID = "radFilterComparisonAnd" + filterCounter;
                        radFilterAnd[filterCounter].GroupName = "radFilterComparison" + filterCounter;
                        if (thisFilter.Comparison == QueryOverrideFilter.ComparisonTypes.And) radFilterAnd[filterCounter].Checked = true;

                        radFilterOr[filterCounter] = new RadioButton();
                        radFilterOr[filterCounter].ID = "radFilterComparisonOr" + filterCounter;
                        radFilterOr[filterCounter].GroupName = "radFilterComparison" + filterCounter;
                        if (thisFilter.Comparison == QueryOverrideFilter.ComparisonTypes.Or) radFilterOr[filterCounter].Checked = true;

                        btnRemove[filterCounter] = new Button();
                        btnRemove[filterCounter].ID = "btnRemove" + filterCounter;
                        btnRemove[filterCounter].Text = "Remove";
                        btnRemove[filterCounter].CssClass = "UserButton";
                        btnRemove[filterCounter].Style.Add("background-color", "#eee");
                        btnRemove[filterCounter].Style.Add("margin-left", "16px");
                        btnRemove[filterCounter].Click += new EventHandler(btnRemove_Click);

                        toolPartPanel.Controls.Add(new LiteralControl("<div id='EditQueryOverrideFilter" + filterCounter + "' style='padding-bottom: 5px;'>"));
                        toolPartPanel.Controls.Add(txtFilterFieldName[filterCounter]);
                        toolPartPanel.Controls.Add(new LiteralControl("<br/>"));
                        toolPartPanel.Controls.Add(txtFilterFieldType[filterCounter]);
                        toolPartPanel.Controls.Add(new LiteralControl("<br/>"));
                        toolPartPanel.Controls.Add(dpdFilterOperator[filterCounter]);
                        toolPartPanel.Controls.Add(new LiteralControl("<br/>"));
                        toolPartPanel.Controls.Add(txtFilterValue[filterCounter]);
                        toolPartPanel.Controls.Add(new LiteralControl("<br/>"));
                        toolPartPanel.Controls.Add(radFilterAnd[filterCounter]);
                        toolPartPanel.Controls.Add(new LiteralControl(" And "));
                        toolPartPanel.Controls.Add(radFilterOr[filterCounter]);
                        toolPartPanel.Controls.Add(new LiteralControl(" Or "));
                        toolPartPanel.Controls.Add(btnRemove[filterCounter]);
                        toolPartPanel.Controls.Add(new LiteralControl("</div>"));
                        toolPartPanel.Controls.Add(new LiteralControl("<div style='width:90%' class='UserDottedLine'></div>"));

                    }

                    QueryOverrideFilter thisDummyFilter = new QueryOverrideFilter();

                    txtNewFilterFieldName.ID = "txtNewFilterFieldName" + filterCounter;
                    txtNewFilterFieldName.CssClass = "ms-input";
                    txtNewFilterFieldName.Style.Add("width", "90%");
                    //txtNewFilterFieldName.Enabled = false;

                    dpdNewFilterOperator.Style.Add("width", "90%");
                    dpdNewFilterOperator.Items.AddRange(thisDummyFilter.PossibleOperators);
                    //dpdNewFilterOperator.Enabled = false;

                    txtNewFilterValue.Style.Add("width", "90%");
                    txtNewFilterValue.CssClass = "ms-input";
                    txtNewFilterValue.ID = "txtNewFilterValue" + filterCounter;
                    //txtNewFilterValue.Enabled = false;

                    btnAdd.ID = "btnAdd";
                    btnAdd.Text = "Add Filter";
                    btnAdd.CssClass = "UserButton";
                    btnAdd.Style.Add("background-color", "#eee");
                    btnAdd.Style.Add("margin-right", "20px");
                    btnAdd.Click += new EventHandler(btnAdd_Click);
                    //btnAdd.Enabled = false
                    
                    toolPartPanel.Controls.Add(new LiteralControl("<strong>New Filter:</strong><br/>"));
                    toolPartPanel.Controls.Add(txtNewFilterFieldName);
                    toolPartPanel.Controls.Add(new LiteralControl("<br/>"));
                    toolPartPanel.Controls.Add(dpdNewFilterOperator);
                    toolPartPanel.Controls.Add(new LiteralControl("<br/>"));
                    toolPartPanel.Controls.Add(txtNewFilterValue);
                    toolPartPanel.Controls.Add(new LiteralControl("<br/>"));
                    toolPartPanel.Controls.Add(new LiteralControl("<div style='text-align:right;'>"));
                    toolPartPanel.Controls.Add(btnAdd);
                    toolPartPanel.Controls.Add(new LiteralControl("</div>"));
                    toolPartPanel.Controls.Add(new LiteralControl("<div id='QueryOverrideContainer' style='text-align: center;'>"));
                    toolPartPanel.Controls.Add(new LiteralControl("<input type='button' class='UserButton' style='margin: 4px;' onClick=\"javascript:openEditBox('EditQueryOverrideContent')\" value=\"View Query Override\"/><br/>"));

                    // Render out the "big" textboxes, with div's to allow them to be floated and hidden as required
                    toolPartPanel.Controls.Add(new LiteralControl("<div id='EditQueryOverrideContent' style='display: none; float: left; position: absolute; right: 100px; top: 200px; padding: 8px; width: 600px; height: 400px; background-color: #eee; border: 1px solid #aaa;'>"));
                    toolPartPanel.Controls.Add(txtQueryOverrideContent);
                    toolPartPanel.Controls.Add(new LiteralControl("<input type='button' value='Done' class='UserButton' onClick=\"javascript:closeEditBox('QueryOverrideContainer', 'EditQueryOverrideContent');\"/>"));
                    toolPartPanel.Controls.Add(new LiteralControl("<span style='text-align: right; width: 530px; position: absolute; font-weight: bold; font-size: 10pt;';>Query Override</span>"));
                    //toolPartPanel.Controls.Add(new LiteralControl("<span style='display: block; text-align: right; font-size: 8pt;'>To create a placeholder in the Query Override, use the syntax {placeholdername}, and the placeholdername will be recognised as a possible data connection for Filter provider Web Parts.</span>"));
                    toolPartPanel.Controls.Add(new LiteralControl("</div>"));

                    toolPartPanel.Controls.Add(new LiteralControl("</div></div>"));

                this.Controls.Add(toolPartPanel);

                }
                catch (Exception ex)
                {
                    this.Controls.Add(new LiteralControl("<span style=\"color: red;\">Error occurred rendering QueryOverride Tool Part: " + ex.Message));
                }
                base.CreateChildControls();

            }

            void btnRemove_Click(object sender, EventArgs e)
            {
                try
                {
                    Button thisButton = (Button)sender;
                    int filterIndexToRemove = int.Parse(thisButton.ID.Replace("btnRemove", ""));

                    // Get a reference to the parent Web Part
                    thisWebPart = (FilteredCQWP)ParentToolPane.SelectedWebPart;

                    ArrayList currentFilters = thisWebPart.QueryOverrideFilters;
                    currentFilters.RemoveAt(filterIndexToRemove);
                    thisWebPart.QueryOverrideFilters = currentFilters;
                    thisWebPart.SetPersonalizationDirty();
                    this.Controls.Clear();
                    this.CreateChildControls();

                }
                catch (Exception ex)
                {
                    this.Controls.Add(new LiteralControl("<span style=\"color: red;\">Error occurred while removing QueryOverride Filter: " + ex.Message));
                }
            }

            void btnAdd_Click(object sender, EventArgs e)
            {
                try
                {
                    QueryOverrideFilter thisNewFilter = new QueryOverrideFilter();
                    int thisOperator = -1;
                    for (int x = 0; x < thisNewFilter.PossibleOperators.Length; x++)
                        if (thisNewFilter.PossibleOperators[x].Value == dpdNewFilterOperator.SelectedValue)
                            thisOperator = x;
                    thisNewFilter.Field = txtNewFilterFieldName.Text;
                    thisNewFilter.Operator = (QueryOverrideFilter.OperatorTypes)thisOperator;
                    thisNewFilter.Value = txtNewFilterValue.Text;

                    thisWebPart = (FilteredCQWP)ParentToolPane.SelectedWebPart;

                    ArrayList currentFilters = thisWebPart.QueryOverrideFilters;
                    currentFilters.Add(thisNewFilter);
                    thisWebPart.QueryOverrideFilters = currentFilters;
                    thisWebPart.SetPersonalizationDirty();
                    this.Controls.Clear();
                    this.CreateChildControls();


                }
                catch (Exception ex)
                {
                    this.Controls.Add(new LiteralControl("<span style=\"color: red;\">Error occurred while adding QueryOverride Filter: " + ex.Message));
                }
            }

            public override void ApplyChanges()
            {

                int filterCounter = -1;

                //thisWebPart.QueryOverrideValue = txtQueryOverrideContent.Text;

                try
                {
                    foreach (QueryOverrideFilter thisFilter in thisWebPart.QueryOverrideFilters)
                    {
                        filterCounter++;
                        
                        int thisOperator = -1;
                        for (int x = 0; x < thisFilter.PossibleOperators.Length; x++)
                            if (thisFilter.PossibleOperators[x].Value == dpdFilterOperator[filterCounter].SelectedValue)
                                thisOperator = x;

                        thisFilter.Field = txtFilterFieldName[filterCounter].Text;
                        thisFilter.Type = txtFilterFieldType[filterCounter].Text;
                        thisFilter.Value = txtFilterValue[filterCounter].Text;
                        thisFilter.Operator = (QueryOverrideFilter.OperatorTypes)thisOperator;
                        if (radFilterAnd[filterCounter].Checked) thisFilter.Comparison = QueryOverrideFilter.ComparisonTypes.And;
                        else if (radFilterOr[filterCounter].Checked) thisFilter.Comparison = QueryOverrideFilter.ComparisonTypes.Or;
                    }

                    thisWebPart.QueryOverrideGroupBy = txtGroupByFields.Text;
                    thisWebPart.QueryOverrideOrderBy = txtOrderByFields.Text;

                    thisWebPart.SetPersonalizationDirty();
                }

                catch (Exception ex)
                {
                    this.Controls.Add(new LiteralControl("<span style=\"color: red;\">Error occurred applying changes from QueryOverride Tool Part: " + ex.Message));
                }
                base.ApplyChanges();
            }
        }

        #endregion

        #region Finally, some strings that should really be moved to static files in the Feature Definition

        private string DefaultHeaderContent()
        {
            string defaultHeader = "<xsl:stylesheet  " + (char)13
                + "  version=\"1.0\"" + (char)13
                + "  exclude-result-prefixes=\"x d ddwrt xsl msxsl\" " + (char)13
                + "  xmlns:x=\"http://www.w3.org/2001/XMLSchema\" " + (char)13
                + "  xmlns:d=\"http://schemas.microsoft.com/sharepoint/dsp\"" + (char)13
                + "  xmlns:ddwrt=\"http://schemas.microsoft.com/WebParts/v2/DataView/runtime\"" + (char)13
                + "  xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\"" + (char)13
                + "  xmlns:msxsl=\"urn:schemas-microsoft-com:xslt\">" + (char)13
                + "  <xsl:template name=\"DefaultHeader\" match=\"*\" mode=\"header\">" + (char)13
                + "    <div class=\"groupheader item medium\" style=\"color: green;\">" + (char)13
                + "      <xsl:call-template name=\"OuterTemplate.GetGroupName\">" + (char)13
                + "        <xsl:with-param name=\"GroupName\" select=\"@*[name()=$Group]\"/>" + (char)13
                + "        <xsl:with-param name=\"GroupType\" select=\"$GroupType\"/>" + (char)13
                + "      </xsl:call-template>" + (char)13
                + "    </div>" + (char)13
                + "  </xsl:template>" + (char)13
                + "</xsl:stylesheet>";
            return defaultHeader;
        }


        private string DefaultItemContent()
        {
            string defaultItem = "<xsl:stylesheet " + (char)13
                + "  version=\"1.0\" " + (char)13
                + "  exclude-result-prefixes=\"x d xsl msxsl cmswrt\"" + (char)13
                + "  xmlns:x=\"http://www.w3.org/2001/XMLSchema\" " + (char)13
                + "  xmlns:d=\"http://schemas.microsoft.com/sharepoint/dsp\" " + (char)13
                + "  xmlns:cmswrt=\"http://schemas.microsoft.com/WebParts/v3/Publishing/runtime\"" + (char)13
                + "  xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" xmlns:msxsl=\"urn:schemas-microsoft-com:xslt\">" + (char)13
                + "    <xsl:template name=\"Default\" match=\"*\" mode=\"itemstyle\">" + (char)13
                + "        <xsl:variable name=\"SafeLinkUrl\">" + (char)13
                + "            <xsl:call-template name=\"OuterTemplate.GetSafeLink\">" + (char)13
                + "                <xsl:with-param name=\"UrlColumnName\" select=\"'LinkUrl'\"/>" + (char)13
                + "            </xsl:call-template>" + (char)13
                + "        </xsl:variable>" + (char)13
                + "        <xsl:variable name=\"SafeImageUrl\">" + (char)13
                + "            <xsl:call-template name=\"OuterTemplate.GetSafeStaticUrl\">" + (char)13
                + "                <xsl:with-param name=\"UrlColumnName\" select=\"'ImageUrl'\"/>" + (char)13
                + "            </xsl:call-template>" + (char)13
                + "        </xsl:variable>" + (char)13
                + "        <xsl:variable name=\"DisplayTitle\">" + (char)13
                + "            <xsl:call-template name=\"OuterTemplate.GetTitle\">" + (char)13
                + "                <xsl:with-param name=\"Title\" select=\"@Title\"/>" + (char)13
                + "                <xsl:with-param name=\"UrlColumnName\" select=\"'LinkUrl'\"/>" + (char)13
                + "            </xsl:call-template>" + (char)13
                + "        </xsl:variable>" + (char)13
                + "        <xsl:variable name=\"LinkTarget\">" + (char)13
                + "            <xsl:if test=\"@OpenInNewWindow = 'True'\" >_blank</xsl:if>" + (char)13
                + "        </xsl:variable>" + (char)13
                + "        <div id=\"linkitem\" class=\"item\">" + (char)13
                + "            <xsl:if test=\"string-length($SafeImageUrl) != 0\">" + (char)13
                + "                <div class=\"image-area-left\"> " + (char)13
                + "                    <a href=\"{$SafeLinkUrl}\" target=\"{$LinkTarget}\">" + (char)13
                + "                        <img class=\"image\" src=\"{$SafeImageUrl}\" alt=\"{@ImageUrlAltText}\" />" + (char)13
                + "                    </a>" + (char)13
                + "                </div>" + (char)13
                + "            </xsl:if>" + (char)13
                + "            <div class=\"link-item\">" + (char)13
                + "	            <xsl:call-template name=\"OuterTemplate.CallPresenceStatusIconTemplate\"/>     " + (char)13
                + "                <a style=\"color: green;\" href=\"{$SafeLinkUrl}\" target=\"{$LinkTarget}\" title=\"{@LinkToolTip}\">" + (char)13
                + "                    <xsl:value-of select=\"$DisplayTitle\"/>" + (char)13
                + "                </a>" + (char)13
                + "                <div class=\"description\">" + (char)13
                + "                    <xsl:value-of select=\"@Description\" />" + (char)13
                + "                </div>" + (char)13
                + "            </div>" + (char)13
                + "        </div>" + (char)13
                + "    </xsl:template>" + (char)13
                + "</xsl:stylesheet>" + (char)13;

            return defaultItem;
        }

        #endregion
    }
}
